package main

import (
	"errors"
	"net/http"
	// "log"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/kotocean/cricket/apiserver/cache"
	"github.com/kotocean/cricket/apiserver/db"
	"github.com/kotocean/cricket/apiserver/token"
)

func main() {
	router := gin.Default()

	authorizedClient := router.Group("/")
	authorizedClient.Use(validateClient())
	{
		authorizedClient.GET("/public", publicHandler)

		// nested group
		authorizedPerson := authorizedClient.Group("special")
		authorizedPerson.Use(validatePerson())
		{
			authorizedPerson.GET("/info", infoHandler)
		}
	}
	router.Run(":8090")
}

func validateClient() gin.HandlerFunc {

	return func(c *gin.Context) {
		clientAuthorization := c.GetHeader("Authorization")

		var claim *token.MyCustomClaims
		var err error
		var values map[string]string
		if clientAuthorization != "" {
			clientToken := strings.Replace(clientAuthorization, "Bearer ", "", 1)
			//检查cache是否存在token
			valuesTemp, errCache := cache.GetToken(clientToken)
			if valuesTemp != nil {
				values = valuesTemp
			}
			if errCache != nil || len(values) == 0 {
				claim, err = token.ValidateToken(clientToken)

				if err == nil {
					// clientToken验证通过
					values["expiresAt"] = strconv.FormatInt(claim.StandardClaims.ExpiresAt, 10)
					values["id"] = strconv.FormatInt(claim.Id, 10)
					values["name"] = claim.Name
					saveErr := cache.SaveToken(clientToken, values)
					if saveErr != nil {
						c.JSON(http.StatusInternalServerError, gin.H{"status": "保存token到cache失败"})
						c.AbortWithStatus(http.StatusUnauthorized)
						return
					}
				}
			}

		} else {
			err = errors.New("Client Token Not Found")
		}

		if err != nil {
			c.Header("WWW-Authenticate", "Authorization Required")
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}
		if claim != nil {
			c.Set("authClientName", claim.Name)
		} else {
			c.Set("authClientName", values["name"])
		}

	}
}

func validatePerson() gin.HandlerFunc {
	return func(c *gin.Context) {
		personToken := c.GetHeader("User-Token")

		var claim *token.MyCustomClaims
		var err error
		var values map[string]string
		if personToken != "" {
			//检查cache是否存在token
			valuesTemp, errCache := cache.GetToken(personToken)
			if valuesTemp != nil {
				values = valuesTemp
			}
			if errCache != nil || len(values) == 0 {
				claim, err = token.ValidateToken(personToken)

				if err == nil {
					// clientToken验证通过
					values["expiresAt"] = strconv.FormatInt(claim.StandardClaims.ExpiresAt, 10)
					values["id"] = strconv.FormatInt(claim.Id, 10)
					values["name"] = claim.Name
					values["delayNum"] = "0"
					//values["accessRegex"] = "" //默认配置为空，即没有任何special特权
					//从数据库读取Person access权限
					person, dbErr := db.GetOrCreatePerson(claim.Id, claim.Name)
					if dbErr != nil {
						//c.JSON(http.StatusInternalServerError, gin.H{"status": "查找数据库personInfo失败"})
						c.AbortWithStatus(http.StatusUnauthorized)
						return
					}
					values["accessRegex"] = person.AccessRegex

					saveErr := cache.SaveToken(personToken, values)
					if saveErr != nil {
						//c.JSON(http.StatusInternalServerError, gin.H{"status": "保存token到cache失败"})
						c.AbortWithStatus(http.StatusUnauthorized)
						return
					}

					//判断是否可访问
					err = validateAccess(c.Request.URL.Path, person.AccessRegex)
				}
			} else {
				//cache 存在

				err = validatePersonToken(personToken, values)

				err = validateAccess(c.Request.URL.Path, values["accessRegex"])
			}

		} else {
			err = errors.New("Person Token Not Found")
		}

		if err != nil {
			c.Header("USER-Authenticate", "User Authorization Required")
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		if claim != nil {
			c.Set("personId", claim.Id)
			c.Set("personName", claim.Name)
		} else {
			c.Set("personId", values["id"])
			c.Set("personName", values["name"])
		}

	}
}

//验证person是否有api访问
func validateAccess(path, accessRegex string) error {

	if accessRegex == "" {
		return errors.New("person不可访问任何的special api")
	}
	re := regexp.MustCompile(accessRegex)
	matched := re.MatchString(path)

	if !matched {
		return errors.New("person没有访问该api的权限")
	}
	return nil
}

//验证person token是否可用
func validatePersonToken(personToken string, values map[string]string) error {
	now := time.Now().Unix()
	expiresAt, _ := strconv.ParseInt(values["expiresAt"], 10, 64)
	delayNum, _ := strconv.ParseInt(values["delayNum"], 10, 64)

	if now <= expiresAt {
		if now+180 >= expiresAt && delayNum < 10 {
			delayNum += 1
			expiresAt += 1800 //s,30m
		}
	} else {
		return errors.New("person token已过期")
	}
	return nil
}

func publicHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"status": "public api", "authClientName": c.MustGet("authClientName")})
}

func infoHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"status": "/special/info"})
}
